#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>

#include "global.h"
#include "memorypool.h"
#include "csocket.h"


void CSocket::eventAccept(pSConnection oldc)
{
    struct sockaddr     mysockaddr;
    socklen_t           socklen = sizeof(mysockaddr);
    int                 err; //暂存错误码
    int                 sockfd; //建立连接的sockfd
    static int          use4 = 1; //系统是否支持accept4，先尝试使用，不支持再切换
    pSConnection        newc; //新建立的连接
    do { //用do...while方便跳出流程
        if (use4)
        {
            sockfd = accept4(oldc->fd, &mysockaddr, &socklen, SOCK_NONBLOCK);
        }
        else
        {
            sockfd = accept(oldc->fd, &mysockaddr, &socklen);
        }

        if (sockfd == -1)
        {

            if (use4 < 0) return; //奇怪的错误

            err = errno;
            switch (err)
            {
                case ENOSYS:
                    use4 -= 1;
                    continue;
                case EAGAIN:
                case ECONNABORTED:
                case EMFILE:
                case ENFILE:
                    LOGSTDERR(errno, "CSocket::eventAccept() accept() error");
                    return ;
            }
        }

        newc = getConnection(sockfd); //注意这个sockfd和监听套接字不是一个东西，是新建立连接后产生的新套接字
        if (newc == nullptr)
        {
            if (close(sockfd) == -1)
            {
                LOGSTDERR(errno, "CSocket::eventAccept() getConnection() error");
            }
            return ;
        }

        memcpy(&newc->s_sockaddr, &mysockaddr, socklen);

        if (!use4)
        {
            //如果没有accept4，需要手动设置非阻塞套接字
            if (setNonBlocking(sockfd) == false) //设置非阻塞失败，返回
            {
                closeConnection(newc);
                return ;
            }
        }

        newc->listening = oldc->listening; //新连接和监听端口绑定
        newc->wrRdy = 1;
        newc->rdHandle = &CSocket::waitReqHandle; //设置回调函数

        if (epollAddEvent(sockfd, 1, 0, 0,
                          EPOLL_CTL_ADD, newc) == -1)
        {
            closeConnection(newc);
            return ;
        }

        break; //到这基本就没什么问题，可以跳出while了
    } while (1);
    return ;
}